﻿using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Source.Plugins
{
    /// <summary>
    /// Override the CRM Fullname format on a contact/lead/user (optional) to be "Salutation First Name Last Name".
    /// 
    /// Plugin Steps:
    /// create - contact - pre-operation - synchronous
    /// update - contact - pre-operation - synchronous - filtering attributes: firstname, lastname, salutation
    /// create - lead - pre-operation - synchronous
    /// update - lead - pre-operation - synchronous - filtering attributes: firstname, lastname, salutation
    /// create - systemuser - pre-operation - synchronous
    /// update - systemuser - pre-operation - synchronous - filtering attributes: firstname, lastname, salutation
    /// </summary>
    public class OverrideCRMFullname : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = factory.CreateOrganizationService(context.UserId);

            if (!context.InputParameters.Contains("Target")) { return; }
            Entity target = context.InputParameters["Target"] as Entity;
            if (target == null) { return; }

            SetFullName(target, context, service);
        }

        private void SetFullName(Entity target, IPluginExecutionContext context, IOrganizationService service)
        {
            Entity record = target;

            // If it's update, and 1 or more values arent in target- get the latest data
            if (context.MessageName.Equals("update", StringComparison.InvariantCultureIgnoreCase) &&
                (!target.Contains("salutation") || !target.Contains("firstname") || !target.Contains("lastname")))
            {
                Entity current = service.Retrieve(target.LogicalName, target.Id, new ColumnSet("salutation", "firstname", "lastname"));
                record = MergeEntities(target, current);
            }

            // Get the values from target or current depending on whether it was in target
            string salutation = record.GetAttributeValue<string>("salutation");
            string firstName = record.GetAttributeValue<string>("firstname");
            string lastName = record.GetAttributeValue<string>("lastname");

            // Join the salutation, first name, and last name (ignoring nulls)
            string fullName = string.Join(" ", new string[] { salutation, firstName, lastName }
                .Where(a => !string.IsNullOrWhiteSpace(a)));

            // Update target, overriding whatever CRM sets
            target["fullname"] = fullName;
        }

        private Entity MergeEntities(Entity primary, Entity secondary)
        {
            // Add attributes from secondary entity if not in primary. Return new entity object so we don't modify target
            Entity merged = new Entity();
            merged.Attributes.AddRange(primary.Attributes);

            secondary.Attributes.ToList().ForEach(a =>
            {
                if (!merged.Contains(a.Key))
                {
                    merged[a.Key] = a.Value;
                }
            });

            return merged;
        }
    }
}
